home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Visual Cafe 3
/
Visual Cafe 3.ISO
/
Vcafe
/
Main.bin
/
JChart.java
< prev
next >
Wrap
Text File
|
1998-11-03
|
38KB
|
1,112 lines
package com.symantec.itools.swing;
import com.sun.java.swing.*;
import com.sun.java.swing.event.*;
import com.sun.java.swing.table.*;
import com.sun.java.swing.border.*;
import java.beans.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
// ===================================================================================
// = Revision History
// ===================================================================================
// 09/25/98 MSH Added resource bundle for localization
// 09/29/98 MSH Added properties yAxisMin, yAxisMax
// 09/30/98 MSH Re-wrote calibrateGraph() to improve support for negative numbers,
// and to add support for yAxisMin, yAxisMax
// 10/21/98 MSH Worked around JIT problem in form designer #67321
/**
* Simple charting component. Creates a chart capable of displaying one or more series
* of data in the form of a bar, line, scatter, pie, or area chart.
*
* The chart uses a standard TableModel interface as the data model type. The TableModel can
* use several types including: Integer, Double, Long, String, and DataElem
*
* @version 1.0, July 28, 1998
*
* @author Michael Hopkins, Symantec
*/
public class JChart extends com.sun.java.swing.JComponent implements GraphStyle, TableModelListener
{
/*** component parameters ***/
protected TableModel model;
protected int graphStyle; // the style of the graph
protected boolean showLegend; // whether legend should be displayed
protected boolean showGrid; // true if a grid should be drawn on the graph
protected String title; // title of the graph
protected int precision; // number of digits of precision to display for data
protected int numTicksY; // the number of ticks on the y axis
protected double yAxisMin; // the preferred starting point of the y axis
protected double yAxisMax; // the preferred maximum value on the y axis
/*** internal data members ***/
protected double graphMin; // the smallest value in the data set
protected double graphMax; // the largest value in the data set
protected double graphUpper; // the upper bounds of the graph on the y axis
protected double graphLower; // the lower bounds of the graph on the y axis
protected int graphCols; // the size of the largest series (number of entries)
protected int gridIncrementV; // the number of pixels between major vertical subdivisions on the graph
protected int gridIncrementH; // the number of pixels between major horizontal subdivisions on the graph
protected double gridStepV; // the value per vertical tick on the y axis
protected int labelYInset; // additional inset on Y axis for labels
protected int titleBorder; // the space above the graph occupied by the title
protected int legendBorder; // the border
protected Insets graphBorder; // the border around the graph
protected boolean debug;
public static final double AUTO_CALCULATE_MIN = Double.NEGATIVE_INFINITY;
public static final double AUTO_CALCULATE_MAX = Double.POSITIVE_INFINITY;
public JChart()
{
model = new DefaultTableModel();
graphStyle = DEFAULT_STYLE;
showLegend = true;
showGrid = true;
title = "";
graphBorder = new Insets( 10, 5, 5, 5 );
precision = 0;
numTicksY = 5;
graphMin = Double.MAX_VALUE;
graphMax = 0;
graphCols = 0;
gridIncrementV = 0;
gridIncrementH = 0;
gridStepV = 0;
labelYInset = 0;
legendBorder = 0;
yAxisMin = 0.0;
yAxisMax = AUTO_CALCULATE_MAX;
titleBorder = 0;
debug = false;
}
/**
* Gets the current model used by the chart
* @return the model
* @see #setModel
*/
public TableModel getModel()
{
return model;
}
/**
* Sets the current model used by the chart. The model is a TableModel derrivative, and can
* be composed of objects of type Integer, Long, Double, String, or DataElem
* @param newModel the TabelModel to be used by the chart
* @exception IllegalArgumentException
* if the specified model is null
* @see #getModel
*/
public void setModel( TableModel newModel )
{
TableModel oldModel = model;
if (newModel == null)
throw new IllegalArgumentException("Cannot set a null TableModel");
if (newModel != oldModel)
{
if (oldModel != null)
oldModel.removeTableModelListener(this);
model = newModel;
newModel.addTableModelListener(this);
}
graphCols = model.getRowCount();
graphMax = seriesMax();
graphMin = seriesMin();
repaint();
}
/**
* Responds to notification of changes in the model data
* @param e the event to process
*/
public void tableChanged(TableModelEvent e)
{
graphCols = model.getRowCount();
graphMax = seriesMax();
graphMin = seriesMin();
repaint();
}
/**
* Get the preferred size of the component
* @return the dimension of the component
*/
public Dimension getPreferredSize( )
{
Dimension d = new Dimension( 200, 150 );
return d;
}
/**
* Get the minimum size of the component
* @return the minimum dimension of the component
*/
public Dimension getMinimumSize()
{
Dimension d = new Dimension( 100, 75 );
return d;
}
/**
* Sets the border of the component
*/
public void setBorder( Border border )
{
super.setBorder( border );
Insets i = getInsets();
graphBorder.top = 10 + i.top;
graphBorder.bottom = 5 + i.bottom;
graphBorder.left = 5 + i.left;
graphBorder.right = 5 + i.right;
repaint();
}
/**
* Draws the contents of the chart component
* @param g the graphics object to be referenced for drawing
*/
public void paintComponent(java.awt.Graphics g)
{
TableModel model = getModel();
Dimension d = getSize();
FontMetrics fm = g.getFontMetrics();
if ( title.length() != 0 )
titleBorder = fm.getHeight() + 4;
else
titleBorder = 0;
Insets i = getInsets();
graphBorder.top = ((g.getFontMetrics().getHeight())/2) + i.top + titleBorder;
graphBorder.bottom = 5 + i.bottom;
graphBorder.left = 5 + i.left;
graphBorder.right = 5 + i.right;
Color oldColor = g.getColor();
g.setColor( getBackground() );
if ( graphStyle != PIE_STYLE )
g.fillRect( graphBorder.left + labelYInset, graphBorder.top,
d.width - graphBorder.left - labelYInset - graphBorder.right - legendBorder ,
d.height - graphBorder.bottom - graphBorder.top );
g.setColor( oldColor );
calcLabelInset( g ); // calculations the position and dimensions of the legend
if ( showLegend == true && graphStyle != PIE_STYLE )
drawLegend( g );
drawTitle( g );
calibrateGraph( ); // determines the scale of the horizontal and vertical graph subdivisions
drawGrid( g );
// plot data
if ( graphStyle < PIE_STYLE ) // draw all types except pie charts and areas
{
int count = getNumSeries(); // the number of series to draw
for ( int k = 0; k < count; k++ )// draw each series
{
int tickPosH = graphBorder.left + labelYInset; // the horizontal location of the series marker
int startPos = tickPosH;
g.setColor( getSeriesColor( k )); // draw in the current series color
int lastValue = 0;
for ( int j = 0; j < getSeriesSize( k ); j++ )
{
double data = getSeriesValue( k, j ) - graphLower;
int plotV = (int)(((data / gridStepV) * gridIncrementV) + .5 );
if ( count > 0 )
{
switch ( graphStyle )
{
case LINE_STYLE:
if ( j > 0 )
g.drawLine( startPos + (j-1) * gridIncrementH, d.height - lastValue - graphBorder.bottom + 1,
startPos + j * gridIncrementH, d.height - plotV - graphBorder.bottom +1 );
case SCATTER_STYLE:
g.fillOval( tickPosH - 2, d.height - plotV - graphBorder.bottom , 4, 4 );
break;
case BAR_STYLE:
g.fillRect( tickPosH + k * (gridIncrementH / count)+2, d.height - plotV - graphBorder.bottom ,
(gridIncrementH / count) - 2, plotV );
break;
default:
return;
}
lastValue = plotV;
tickPosH += gridIncrementH;
}
}
}
}
else
if ( graphStyle == AREA_STYLE )
drawAreaGraph( g );
else
drawPieGraph( g );
if ( graphStyle != PIE_STYLE )
drawAxis( g );
if ( Beans.isDesignTime() ) // work around for JIT problem
drawTitle( g ); // applet will work fine in browser
}
/********** Property Accessor Routines **********/
/**
* Gets the new graph style.
* @return the new border style, one of DEFAULT_STYLE, LINE_STYLE,
* BAR_STYLE, PIE_STYLE, AREA_STYLE, or SCATTER_STYLE
* @see #setStyle
* @see GraphStyle#DEFAULT_STYLE
* @see GraphStyle#LINE_STYLE
* @see GraphStyle#BAR_STYLE
* @see GraphStyle#PIE_STYLE
* @see GraphStyle#AREA_STYLE
* @see GraphStyle#SCATTER_STYLE
*/
public int getStyle()
{
return graphStyle;
}
/**
* Sets the new graph style.
* @param style the new border style, one of DEFAULT_STYLE, LINE_STYLE,
* BAR_STYLE, PIE_STYLE, AREA_STYLE, or SCATTER_STYLE
* @see #getStyle
* @see GraphStyle#DEFAULT_STYLE
* @see GraphStyle#LINE_STYLE
* @see GraphStyle#BAR_STYLE
* @see GraphStyle#PIE_STYLE
* @see GraphStyle#AREA_STYLE
* @see GraphStyle#SCATTER_STYLE
*/
public void setStyle( int style )
{
if ( style <= AREA_STYLE == style >= DEFAULT_STYLE )
{
graphStyle = style;
repaint();
}
}
/**
* returns whether or not the graph is to display a legend
* @return true if graph displays a legend
* @see #setShowLegend
*/
public boolean isShowLegend()
{
return showLegend;
}
/**
* Sets whether the graph is to display a legend or not
* @param b true if graph should display a legend
* @see #isShowLegend
*/
public void setShowLegend( boolean b )
{
showLegend = b;
repaint();
if ( b == false )
legendBorder = 0;
}
/**
* returns whether or not a grid is displayed as an overlay of the data area
* @return true if a grid is displayed
* @see #getShowGrid
*/
public boolean isShowGrid( )
{
return showGrid;
}
/**
* returns whether or not a grid is displayed as an overlay of the data area
* @return true if a grid is displayed
* @see #setShowGrid
* @deprecated use isShowGrid instead
*/
public boolean getShowGrid( )
{
return isShowGrid();
}
/**
* sets whether or not a grid should be displayed as an overlay of the data area
* @param show true if a grid should be displayed
* @see #getShowGrid
*/
public void setShowGrid( boolean show )
{
showGrid = show;
repaint();
}
/**
* Gets the title of the graph
* @return String title of the graph
* @see #setTitle
*/
public String getTitle( )
{
return title;
}
/**
* Sets the title of the graph
* @param s title of the graph
* @see #getTitle
*/
public void setTitle( String s )
{
title = s;
repaint();
}
/**
* gets border insets of the graph
* @return border of the graph
* @see #setGraphBorder
*/
public Insets getGraphBorder( )
{
return graphBorder;
}
/**
* sets the border insets of the graph
* @param i graph insets to use
* @see #getGraphBorder
*/
public void setGraphBorder( Insets i )
{
graphBorder = i;
}
/**
* gets the precision displayed by the chart. Precision is defined
* as the number of digits after the decimal point that are to be displayed.
* specifying zero indicates that the number is to be treated as an integer.
* @return int the number of places displayed after the decimal
* @see #setPrecision
*/
public int getPrecision( )
{
return precision;
}
/**
* sets the precision to be displayed by the chart. Precision is defined
* as the number of digits after the decimal point that are to be displayed.
* specifying zero indicates that the number is to be treated as an integer.
* @param places the number of places to display after the decimal
* @see #getPrecision
*/
public void setPrecision( int places )
{
precision = places;
repaint();
}
/**
* gets the number of ticks or major subdivisions displayed on the y axis
* @return int the number of major subdivisions of the y axis
* @see #setNumYTicks
*/
public int getNumYTicks( )
{
return numTicksY;
}
/**
* sets the number of ticks or major subdivisions to be displayed on the y axis
* @param ticks the number of major subdivisions to use for the y axis
* @see #getNumYTicks
*/
public void setNumYTicks( int ticks )
{
numTicksY = ticks;
repaint();
}
/**
* gets the minimum or origin value on the y axis
* @return the minimum or origin value on the y axis
* @see #setYAxisMin
*/
public double getYAxisMin()
{
return yAxisMin;
}
/**
* sets the minimum or origin value on the y axis
* @param min the minimum or origin value on the y axis
* @see #getYAxisMin
*/
public void setYAxisMin( double min )
{
yAxisMin = min;
repaint(); // calls calibrate graph
}
/**
* gets the maximum on the y axis
* @return the maximum value on the y axis
* @see #setYAxisMax
*/
public double getYAxisMax()
{
return yAxisMax;
}
/**
* sets the maximum on the y axis
* @param max the maximum value on the y axis
* @see #getYAxisMax
*/
public void setYAxisMax( double max )
{
yAxisMax = max;
repaint(); // calls calibrate graph
}
/********** Data Acessor Routines **********/
/**
* Returns the number of data series that are used in the graph
* @return number of series
*/
public int getNumSeries( )
{
return getModel().getColumnCount();
}
/**
* Gets the size (number of elements) of the series referenced by index i
* @param i the index of the series
* @return the size of the series
* @exception ArrayIndexOutOfBoundsException
*/
public int getSeriesSize( int i )
{
return getModel().getRowCount();
}
/**
* retrieves a series entry value
* @param series the number of the series to edit
* @param index the index of the item to edit
* @exception ArrayIndexOutOfBoundsException
*/
public double getSeriesValue( int series, int index )
{
Object obj = getModel().getValueAt(index, series);
if ( obj != null )
{
try
{
if ( obj instanceof DataElem )
return ((DataElem)obj).getData();
else if ( obj instanceof Double )
return ((Double) obj).doubleValue();
else if ( obj instanceof Integer )
return (double)((Integer) obj).intValue();
else if ( obj instanceof Long )
return (double)((Long)obj).intValue();
else if ( obj instanceof String )
return (new Double((String)obj)).doubleValue();
}
catch ( Exception e )
{
System.out.println( "Warning- Non-numeric detected in chart data: " + obj );
}
}
return 0;
}
/**
* gets the name of a series associated with a given index
* @param series index of the series to use
* @return name of the requested series
*/
public String getSeriesName( int series )
{
return getModel().getColumnName( series );
}
/**
* gets the default color of a particular series
* @param series index of the series to use
* @return color associated with the requested series
*/
public Color getSeriesColor( int series )
{
return getSeriesColor( series, 0 );
}
/**
* gets the color of a particular data element within a series
* @param series number of the series to use
* @param index associated with the data element within the series
* @return color associated with the requested series
*/
public Color getSeriesColor( int series, int index )
{
Object obj = getModel().getValueAt(index, series);
if ( obj != null )
{
if ( obj instanceof DataElem )
return ((DataElem)obj).getColor();
}
Vector colors = DataElem.graphColors;
if ( graphStyle != PIE_STYLE )
return (java.awt.Color) colors.elementAt( series % colors.size());
return ( java.awt.Color ) colors.elementAt( ((series * graphCols) + index) % colors.size() );
}
/**
* gets the label of a data element associated with a given
* index of a specific series
* @param series number of the series to use
* @param index of the data element to use
* @return name of the requested series
* @see #getSeriesName
*/
public String getDataLabel( int series, int index )
{
Object obj = getModel().getValueAt(index, series);
if ( obj != null )
{
if ( obj instanceof DataElem )
return ((DataElem)obj).getLabel();
}
return "";
}
/********** Data Statistical Routines **********/
/**
* finds the highest value (max) in all series
* @return max value in given series
* @exception ArrayIndexOutOfBoundsException
* @see #seriesMax( int num )
*/
protected double seriesMax( )
{
double maxVal = 0;
for ( int i = 0; i < getNumSeries(); i++ )
maxVal = Math.max( maxVal, seriesMax( i ));
return maxVal;
}
/**
* finds the highest value (max) in a series
* @param num the series in which to find the maximum value
* @return max value in given series
* @exception ArrayIndexOutOfBoundsException
*/
protected double seriesMax( int num )
{
double maxVal = 0;
for ( int i = 0; i < getSeriesSize( num ); i++ )
maxVal = Math.max( maxVal, getSeriesValue( num, i ));
return maxVal;
}
/**
* finds the lowest value (min) in all series
* @return min value in given series
* @exception ArrayIndexOutOfBoundsException
* @see #seriesMin( int num )
*/
protected double seriesMin( )
{
double minVal = Double.MAX_VALUE;
for ( int i = 0; i < getNumSeries(); i++ )
minVal = Math.min( minVal, seriesMin( i ));
return minVal;
}
/**
* finds the lowest value (min) in a series
* @param num the series in which to find the minimum value
* @return min value in given series
* @exception ArrayIndexOutOfBoundsException
*/
protected double seriesMin( int num )
{
double minVal = getSeriesValue( num, 0 );
for ( int i = 1; i < getSeriesSize( num ); i++ )
minVal = Math.min( minVal, getSeriesValue( num, i ));
return minVal;
}
/**
* finds the maximum of the summation of all series entries at a given index
* @return maximum graph summation
*/
protected double seriesSumMax( )
{
double max = 0;
for ( int i = 0; i < graphCols; i++ )
{
double tempMax = 0;
for ( int j = 0; j < getNumSeries(); j++ )
{
if ( i < getSeriesSize( j ) )
tempMax += getSeriesValue( j, i );
}
max = Math.max( max, tempMax );
}
return max;
}
/**
* finds the sumation of all data points in a given series
* @param num the series in which to find the sumation
* @return sumation of a given series
*/
protected double seriesSum( int num )
{
double sum = getSeriesValue( num, 0 );
for ( int i = 1; i < getSeriesSize( num ); i++ )
sum += getSeriesValue( num, i );
return sum;
}
/********** Graphics utility routines **********/
protected String truncDataStr( String s )
{
StringBuffer buf = new StringBuffer( s );
int i = s.indexOf( '.' );
if ( i > 0 )
if ( precision == 0 )
buf.setLength( i );
else if ( buf.length() >= i + 1+ precision )
buf.setLength( i + 1 + precision );
return buf.toString();
}
protected void calcLabelInset( java.awt.Graphics g )
{
FontMetrics fm = g.getFontMetrics();
Insets i = getInsets();
if ( graphStyle != AREA_STYLE )
labelYInset = fm.stringWidth( truncDataStr( String.valueOf( graphMax ))) - 1;
else
labelYInset = fm.stringWidth( truncDataStr( String.valueOf( seriesSumMax() ))) - 1;
}
/**
* Calculates the interval between tick marks on the horizontal axis
*/
protected void calcTickInterval()
{
if ( graphStyle == BAR_STYLE )
gridIncrementH = (getSize().width - graphBorder.right - graphBorder.left - labelYInset - legendBorder )/
(graphCols > 0 ? graphCols : 1);
else
gridIncrementH = (getSize().width - graphBorder.right - graphBorder.left - labelYInset - legendBorder )/
(graphCols-1 > 0 ? graphCols-1 : 1);
}
protected void calibrateGraph( ) // find y axis calibration
{
calcTickInterval();
if ( yAxisMax != AUTO_CALCULATE_MAX )
graphUpper = yAxisMax;
else
{
if ( graphStyle != AREA_STYLE )
graphUpper = seriesMax();
else
graphUpper = seriesSumMax();
}
debugStr( "graphUpper: " + graphUpper );
if ( precision == 0 )
gridStepV = Math.ceil(graphUpper/numTicksY);
else
gridStepV = graphUpper / numTicksY;
debugStr( "gridStepV: " + gridStepV );
if ( yAxisMax == AUTO_CALCULATE_MAX )
graphUpper = gridStepV * numTicksY;
// if the user has provided an upper and a lower limit, use them
if ( yAxisMin != AUTO_CALCULATE_MIN )
graphLower = yAxisMin;
else
{
graphLower = seriesMin();
graphLower = Math.floor( graphLower/gridStepV ) * gridStepV;
if ( graphLower > 0 && (graphLower / ( seriesMax() - seriesMin()) <= .2 ))
graphLower = 0;
}
//gridStepV = (graphUpper - graphLower)/numTicksY;
Dimension d = getSize();
gridIncrementV= (int)(((d.height - graphBorder.bottom - graphBorder.top ) / numTicksY)+.5);
}
protected void drawAreaGraph( java.awt.Graphics g )
{
Dimension d = getSize();
int plotV = 0;
double lastValue = 0;
Vector polyArray = new Vector( getNumSeries() );
int tickPosH = graphBorder.left + labelYInset;
for ( int i = 0; i < graphCols; i++ )
{
for ( int j = 0; j < getNumSeries(); j++ )
{
if ( i == 0 ) // we only want to create a polygon once for each series
{
Polygon poly = new Polygon();
poly.addPoint( graphBorder.left + labelYInset, d.height - graphBorder.bottom );
polyArray.addElement((Object) poly);
}
if ( i < getSeriesSize( j ) )
{
double data = getSeriesValue( j, i ) - graphLower;
plotV = (int)(((data / gridStepV) * gridIncrementV) + .5 );
plotV += lastValue;
}
Polygon p = (Polygon) polyArray.elementAt( j );
p.addPoint( tickPosH, d.height - plotV - graphBorder.bottom );
lastValue = plotV;
}
tickPosH += gridIncrementH;
plotV = 0;
lastValue = 0;
}
for ( int k = getNumSeries()-1; k >= 0; k-- )
{
Polygon tempPoly = (Polygon) polyArray.elementAt( k );
tempPoly.addPoint( graphBorder.left + labelYInset + (graphCols-1) * gridIncrementH, d.height - graphBorder.bottom );
g.setColor( getSeriesColor( k ));
g.fillPolygon( tempPoly );
}
}
protected void drawPieGraph( java.awt.Graphics g )
{
Dimension d = getSize();
Rectangle graphArea = new Rectangle( d.width - graphBorder.left - graphBorder.right,
d.height - graphBorder.top - graphBorder.bottom );
int minDimension = Math.min( graphArea.width, graphArea.height );
if ( minDimension <= 0 )
return;
Rectangle cell = new Rectangle( minDimension, minDimension );
int num = getNumSeries();
if ( num < 1 )
return;
int rows = 1;
int cols = num;
int pad = 30;
int rem = 0;
int first= num;
FontMetrics fm = g.getFontMetrics();
int vLabel = fm.getHeight() + 10;
boolean done = false;
cell.width = (int)Math.floor((graphArea.width - pad * ( num - 1 )) / (double) num); // fit cells horizontally
cell.height= (int)Math.floor((graphArea.height- pad * ( rows - 1 )) / (double) rows - vLabel);
cell.width = cell.height = Math.min( cell.width, cell.height );
cell.height += vLabel;
while ( !done ) // figure out how to tile the pie charts
{
if ( (rows+1) * cell.height + rows * pad < graphArea.height ) // is there room for another row?
{
rows++; // create a new row
cols = num / rows; // compute average row count
rem = num - (cols * rows); // compute remainder
if ( rem > 1 )
{
cols += 1;
first = cols;
}
else
first = cols + rem;
cell.width = (int)Math.floor((graphArea.width - pad * ( first - 1 )) / (double) first); // fit cells horizontally
cell.height= (int)Math.floor((graphArea.height- pad * ( rows - 1 )) / (double) rows - vLabel);
cell.width = cell.height = Math.min( cell.width, cell.height );
cell.height += vLabel;
}
else
done = true;
}
int n = 1;
int last = 0;
int hSpace = ((graphArea.width - ( cols * cell.width + ( cols - 1 ) * pad ))/cols)/2;
int vSpace = ((graphArea.height - ( rows * cell.height + ( rows - 1 ) * pad ))/rows)/2;
for ( int y = 0; y < rows; y++ )
{
for ( int x = 0; x < first; x++ )
{
if ( n > num )
break;
if ( y != 0 && x >= cols )
continue;
cell.setLocation( graphArea.x + graphBorder.left + ( cell.width + ((x != 0 ) ? pad : 0 )) *x + hSpace,
graphArea.y + graphBorder.top + (cell.height + ((y != 0 ) ? pad : 0 )) * y + vSpace );
drawPieSeries( g, n-1, cell, last, vLabel );
n++;
}
last = 0;
}
}
/**
* Draws the legend of the graph
*/
protected void drawLegend( java.awt.Graphics g )
{
int maxStrLen = 0; // the length of the longest series name string
Rectangle lRect; // the rectangle of the legend
FontMetrics fm = g.getFontMetrics(); // the font metrics of the Graphics object
int lnHeight = fm.getHeight(); // the height of a single line of text in the legend
int boxHeight; // the height of the legend box including text str
int count = getNumSeries(); // the series count
Dimension d = getSize(); // the dimension of the canvas
ResourceBundle conn = ResourceBundle.getBundle("com.symantec.itools.swing.JChartResourceBundle");
if ( count <= 0 )
return;
for ( int i = 0; i < count; i++ ) // find the length of the longest series name
{
String name = getSeriesName( i );
if ( name == "" || name == " " )
name = conn.getString(JChartResourceBundle.JCHART_SERIES_KEY) + " " + (i+1);
maxStrLen = Math.max( fm.stringWidth( name ), maxStrLen );
}
maxStrLen = Math.max( maxStrLen, fm.stringWidth("Legend "));
legendBorder = maxStrLen + 22;
boxHeight = count * (lnHeight + 4) + 12;
while ( boxHeight + lnHeight + 4 > d.height )
{
boxHeight -= (lnHeight + 4);
count--;
}
int yPos = (d.height / 2) - (boxHeight/2) - 4;// the upper left corner of the legend (centered)
lRect = new Rectangle( d.width - graphBorder.right - legendBorder + fm.getHeight()/2 + 2, yPos +4,
legendBorder - fm.getHeight()/2 - 4, boxHeight );
g.drawString( conn.getString(JChartResourceBundle.JCHART_LEGEND_KEY), lRect.x + (lRect.width - fm.stringWidth( "Legend" ))/2, lRect.y - 5 );
g.drawRect( lRect.x, lRect.y, lRect.width, lRect.height - 10 );
int ovalSize = fm.getHeight()/4;
ovalSize = ovalSize < 4 ? 4 : ovalSize;
for ( int j = 0; j < count; j++ )
{
yPos += lnHeight + 4;
g.setColor( getSeriesColor( j ));
g.fillOval( lRect.x + 6 - (ovalSize/2), yPos - (lnHeight / 2 ), ovalSize, ovalSize );
g.setColor( Color.black );
String s = getSeriesName( j );
if ( s == "" || s == " " )
s = "Series " + (j+1);
g.drawString( s, lRect.x + 10, yPos );
}
}
protected void drawPieSeries( java.awt.Graphics g, int seriesNum, Rectangle frame, int startAngle, int vLabel )
{
double total = seriesSum( seriesNum );
double err = 0;
if ( getNumSeries() <= 0 || total == 0 )
return;
Font oldFont = g.getFont();
int size = oldFont.getSize();
int style= oldFont.getStyle();
String fnt= oldFont.getName();
Font newFont = new Font( fnt, graphStyle, 9 );
g.setFont( newFont );
for ( int i = 0; i < getSeriesSize( seriesNum ); i++ )
{
double data = getSeriesValue( seriesNum, i );
double percent = data / total;
int arc = (int)((percent * 360)+.5);
err += (percent * 360) - arc;
if ( arc >= 1 )
{
arc++; err--;
}
else if ( arc <= -1 )
{
arc--; err++;
}
g.setColor( (java.awt.Color) getSeriesColor( seriesNum, i ));
//g.setColor( (java.awt.Color) graphColors.elementAt( (( seriesNum * graphCols ) + i ) % graphColors.size()));
g.fillArc( frame.x, frame.y, frame.width, frame.height - vLabel, startAngle, arc );
if ( arc > 3 ) // do not label segments less than 3 degrees
{
g.setColor( Color.black );
int tic = startAngle + arc / 2;
double x = Math.cos((double)tic*(Math.PI/180)) * ((frame.width/2)+10);
double y = Math.sin((double)tic*(Math.PI/180)) * ((frame.width/2)+10);
g.drawString( getDataLabel( seriesNum, i ), (int)x + frame.width/2 + frame.x - 2, frame.width/2 + frame.y - (int)y + 5 );
}
startAngle += arc;
}
g.setFont( oldFont );
g.setColor( Color.black );
g.drawOval( frame.x, frame.y, frame.width, frame.height - vLabel );
FontMetrics fm = g.getFontMetrics();
g.drawString( getSeriesName( seriesNum ), (frame.width - fm.stringWidth( getSeriesName( seriesNum )))/2 + frame.x,
frame.y + frame.height );
}
protected void drawTitle( java.awt.Graphics g )
{
Dimension d = getSize();
FontMetrics fm = g.getFontMetrics();
g.drawString( title, (d.width - graphBorder.right - graphBorder.right - legendBorder - labelYInset - fm.stringWidth( title ))/2
+ graphBorder.left + labelYInset, titleBorder );
}
protected void drawGrid( java.awt.Graphics g )
{
if ( showGrid && graphStyle != PIE_STYLE )
{
Dimension d = getSize(); // the dimension of the canvas
Color oldColor = g.getColor();
g.setColor( Color.lightGray );
for ( int i = 1; i <= numTicksY; i++ )
g.drawLine( graphBorder.left + labelYInset + 2, d.height - graphBorder.bottom - gridIncrementV * i,
d.width - graphBorder.right - legendBorder, d.height - graphBorder.bottom - gridIncrementV * i);
int tickPosH = graphBorder.left + labelYInset;
for ( int i = 1; i <= (graphStyle == BAR_STYLE ? graphCols : graphCols-1 ); i++ )
{
g.drawLine( tickPosH + gridIncrementH, d.height - graphBorder.bottom - 2, tickPosH + gridIncrementH, d.height - graphBorder.bottom + 2 );
g.drawLine( tickPosH + gridIncrementH, d.height - graphBorder.bottom - 3, tickPosH + gridIncrementH, graphBorder.top );
tickPosH += gridIncrementH;
}
g.setColor( oldColor );
}
}
protected void drawAxis( java.awt.Graphics g )
{
Dimension d = getSize(); // the dimension of the canvas
g.setColor( Color.black );
g.drawLine( graphBorder.left + labelYInset, graphBorder.top, graphBorder.left + labelYInset, d.height - graphBorder.bottom + 3 ); // y axis
g.drawLine( graphBorder.left + labelYInset -3, d.height - graphBorder.bottom, d.width - graphBorder.right - legendBorder, d.height - graphBorder.bottom );
drawYAxisTicks( g );
// draw ticks evenly distributed on x axis for series values
int tickPosH = graphBorder.left + labelYInset;
for ( int i = 1; i <= (graphStyle != BAR_STYLE ? graphCols-1 : graphCols ); i++ )
{
g.drawLine( tickPosH + gridIncrementH, d.height - graphBorder.bottom - 2, tickPosH + gridIncrementH, d.height - graphBorder.bottom + 2 );
tickPosH += gridIncrementH;
}
}
protected void drawYAxisTicks( java.awt.Graphics g )
{
g.setColor( Color.black );
FontMetrics fm = g.getFontMetrics();
Dimension d = getSize(); // the dimension of the canvas
if ( graphCols > 0 )
for ( int i = 0; i <= numTicksY; i++ )
{
g.drawLine( graphBorder.left + labelYInset - 2, d.height - graphBorder.bottom - gridIncrementV * i,
graphBorder.left + labelYInset + 2, d.height - graphBorder.bottom - gridIncrementV * i );
String s = truncDataStr( String.valueOf( gridStepV * i + (int)graphLower ));
g.drawString( s, graphBorder.left + labelYInset - fm.stringWidth( s ) - 2,
d.height - graphBorder.bottom - gridIncrementV * i + 3 );
}
}
/********** Utility Routines **********/
void debugStr( String msg )
{
if ( debug )
System.out.println( msg );
}
String styleString( )
{
if ( graphStyle == LINE_STYLE )
return "line";
else if ( graphStyle == BAR_STYLE )
return "bar";
else if ( graphStyle == SCATTER_STYLE )
return "scatter";
else if ( graphStyle == AREA_STYLE )
return "area";
else if ( graphStyle == PIE_STYLE )
return "pie";
return "other";
}
}